package com.nulldev.util.uri.yt.broken.util;

public abstract class Subscriber<T> implements Observer<T>, Subscription {
    private static final long NOT_SET = Long.MIN_VALUE;
    private final SubscriptionList subscriptions;
    private final Subscriber<?> subscriber;
    private Producer producer;
    private long requested;
    
    protected Subscriber() {
        this(null, false);
    }
    
    protected Subscriber(final Subscriber<?> subscriber) {
        this(subscriber, true);
    }
    
    protected Subscriber(final Subscriber<?> subscriber, final boolean shareSubscriptions) {
        this.requested = Long.MIN_VALUE;
        this.subscriber = subscriber;
        this.subscriptions = ((shareSubscriptions && subscriber != null) ? subscriber.subscriptions : new SubscriptionList());
    }
    
    public final void add(final Subscription s) {
        this.subscriptions.add(s);
    }
    
    @Override
    public final void unsubscribe() {
        this.subscriptions.unsubscribe();
    }
    
    @Override
    public final boolean isUnsubscribed() {
        return this.subscriptions.isUnsubscribed();
    }
    
    public void onStart() {
    }
    
    protected final void request(final long n) {
        if (n < 0L) {
            throw new IllegalArgumentException("number requested cannot be negative: " + n);
        }
        final Producer producerToRequestFrom;
        synchronized (this) {
            if (this.producer == null) {
                this.addToRequested(n);
                return;
            }
            producerToRequestFrom = this.producer;
        }
        producerToRequestFrom.request(n);
    }
    
    private void addToRequested(final long n) {
        if (this.requested == Long.MIN_VALUE) {
            this.requested = n;
        }
        else {
            final long total = this.requested + n;
            if (total < 0L) {
                this.requested = Long.MAX_VALUE;
            }
            else {
                this.requested = total;
            }
        }
    }
    
    public void setProducer(final Producer p) {
        boolean passToSubscriber = false;
        final long toRequest;
        synchronized (this) {
            toRequest = this.requested;
            this.producer = p;
            if (this.subscriber != null && toRequest == Long.MIN_VALUE) {
                passToSubscriber = true;
            }
        }
        if (passToSubscriber) {
            this.subscriber.setProducer(this.producer);
        }
        else if (toRequest == Long.MIN_VALUE) {
            this.producer.request(Long.MAX_VALUE);
        }
        else {
            this.producer.request(toRequest);
        }
    }
}
